package com.google.code.solrdimension.queryresponse;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.FacetField.Count;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.code.solrdimension.config.DimensionConfigWrapper;
import com.google.code.solrdimension.config.StaticConfiguration;
import com.google.code.solrdimension.config.dimension.DimensionConfig;
import com.google.code.solrdimension.config.dimensionvalue.AbstractDimensionValueConfig;
import com.google.code.solrdimension.config.dimensionvalue.ExactDimensionValueConfig;
import com.google.code.solrdimension.core.DimensionResponse;
import com.google.code.solrdimension.core.dimensions.Dimension;
import com.google.code.solrdimension.core.dimensions.DimensionValue;
import com.google.code.solrdimension.parsers.exception.SolrDimensionException;
import com.google.code.solrdimension.solrqueryutil.QueryUtil;

/**
 * The {@link QueryResponseInterpreterImpl} translates the facet and values to
 * {@link Dimension} and {@link DimensionValue}
 * 
 * Given that the {@link DimensionConfigWrapper} is always static in SolrDimension,
 * two {@link Map}s are used for
 * 
 * "caching" the mapping of {@link DimensionConfig} ids to
 * {@link DimensionConfig} instances and the mapping of
 * {@link AbstractDimensionValueConfig} ids to
 * {@link AbstractDimensionValueConfig} instances
 * 
 * @author mhc
 * 
 */
public class QueryResponseInterpreterImpl implements QueryResponseInterpreter {

    private Map<String, DimensionConfig> dimensionConfigMap = null;

    private Map<String, AbstractDimensionValueConfig> dimensionValueConfigMap = null;

    private static final Logger logger = LoggerFactory.getLogger(QueryResponseInterpreterImpl.class);

    public DimensionResponse interpretQueryResponse(QueryResponse solrQueryResponse, DimensionConfigWrapper config) throws SolrDimensionException {

        if (dimensionConfigMap == null || dimensionValueConfigMap == null) {
            initMaps(config);
        }

        DimensionResponse solrDimensionResponse = new DimensionResponse();

        // get all facet fields
        List<FacetField> facetFields = solrQueryResponse.getFacetFields();

        // get params to get facet filter queries
        SimpleOrderedMap<String> params = (SimpleOrderedMap<String>) solrQueryResponse.getHeader().get(StaticConfiguration.SOLR_RESPONSE_PARAMS);

        // This is for producing the path
        Object filterQueries = params.get(StaticConfiguration.SOLR_RESPONSE_FILTER_QUERY);

        Map<String, String> activeFacetPrefixes = QueryUtil.getFacetPrefixFromQueryResponse(solrQueryResponse);

        for (FacetField facetField : facetFields) {

            List<Count> counts = facetField.getValues();
            Map<String, DimensionValue> dimensionValues = new LinkedHashMap<String, DimensionValue>();
            long dimensionCount = 0;

            if (counts != null && !counts.isEmpty()) {
                
                
                
                for (Count count : counts) {

                    if (count.getCount() > 0) {

                        String dimensionValueName = null;
                        String facetPrefixQuery = null;
                        // only check for prefixes if there is a Prefix
                        // separator
                        if (count.getName().contains(StaticConfiguration.SEPARATOR)) {

                            logger.debug("Facet value: " + count.getName() + " contains prefix");
                            
                            if (activeFacetPrefixes != null && !activeFacetPrefixes.isEmpty()) {
                                
                                // check if the field is prefixed
                                if (activeFacetPrefixes.containsKey(facetField.getName()) && count.getName().startsWith(activeFacetPrefixes.get(facetField.getName()))) {

                                    logger.debug("Facet prefix found for field: " + facetField.getName() + " and matches facet value: " + count.getName());

                                    // Cut out the prefix, since it is selected
                                    dimensionValueName = count.getName().split(StaticConfiguration.SEPARATOR_REGEX)[1];

                                }

                            }
                            
                            //After checking the prefixes, if the dimensionValueName is still null, this probably
                            //means the active facet prefixes is not on this facet value, so ONLY display the first part
                            //And we need to retrieve the display name from the dimensionConfig
                            if (dimensionValueName == null){
                                String prefix = count.getName().split(StaticConfiguration.SEPARATOR_REGEX)[0];
                                facetPrefixQuery = facetField.getName() + StaticConfiguration.COLON + prefix;
                                dimensionValueName = getDimensionValueDisplayName(prefix);
                            }
                            
                           
                        } else {
                            dimensionValueName = getDimensionValueDisplayName(count.getName());
                        }
                        
                        dimensionCount++;
                        
                        //This only occurs for sift dimension, since no DimensionValue can have the same name
                        //if there is a same name, this is a prefix 
                        if (dimensionValues.containsKey(dimensionValueName)){
                            
                            DimensionValue sameNameDimVal = dimensionValues.get(dimensionValueName);
                            
                            sameNameDimVal.setCount(sameNameDimVal.getCount() + count.getCount());
                            
                        } else {
                            DimensionValue dimensionValue = new DimensionValue(dimensionValueName, count.getAsFilterQuery(), facetPrefixQuery, count.getCount());
                            dimensionValues.put(dimensionValueName, dimensionValue);
                            
                        }
                       
                        

                    }

                }

            }

            // only create a new dimension and add it to solrDimensionResponse
            // if there are values in it.

            if (!dimensionValues.isEmpty()) {

                Dimension dimension = new Dimension(getDimensionDisplayName(facetField.getName()), dimensionCount);

                if ((dimensionConfigMap.get(facetField.getName()) != null) && DimensionConfig.ALPHA_VALUE_ORDER.equals(dimensionConfigMap.get(facetField.getName()).getValueOrder())){
                    
                    ArrayList<DimensionValue> dimVals = new ArrayList<DimensionValue>(dimensionValues.values());
                   //sort alpha
                    Collections.sort(dimVals);
                    
                    dimension.setDimensionValues(dimVals);
                    
                } else {
                    dimension.setDimensionValues(new ArrayList<DimensionValue>(dimensionValues.values()));
                }
                
                

                solrDimensionResponse.addDimension(dimension);

            }

        }

        return solrDimensionResponse;
    }

    private void initMaps(DimensionConfigWrapper config) {

        List<DimensionConfig> dimensionConfigList = config.getDimensionConfigs();

        dimensionConfigMap = new HashMap<String, DimensionConfig>();
        dimensionValueConfigMap = new HashMap<String, AbstractDimensionValueConfig>();

        for (DimensionConfig dimensionConfig : dimensionConfigList) {

            // DimensionConfigId has different prefix, need to get the first
            // instance of AbstractDimensionValueConfig
            // To determine the Prefix type
            String dimensionConfigId = null;
            
            if (dimensionConfig.getDimensionValueConfigs() != null){
                
                for (AbstractDimensionValueConfig dimensionValueConfig : dimensionConfig.getDimensionValueConfigs()) {
                    
                    if (dimensionConfigId == null) {
                        
                        dimensionConfigId = dimensionConfig.getId();
                        
                        if (dimensionValueConfig instanceof ExactDimensionValueConfig) {

                            dimensionConfigId = StaticConfiguration.FIELD_PREFIX + dimensionConfigId;

                        } else {

                            dimensionConfigId = StaticConfiguration.MULTI_VALUED_FIELD_PREFIX + dimensionConfigId;

                        }

                    }

                    dimensionValueConfigMap.put(dimensionValueConfig.getDimensionValueId(), dimensionValueConfig);
                }
            } else {
                
                //this is for dimensionConfig in "default" mode
                
                dimensionConfigId = dimensionConfig.getId();
            }
            

            dimensionConfigMap.put(dimensionConfigId, dimensionConfig);

        }

    }
    
    private String getDimensionDisplayName(String facetFieldName){
        
        String dimensionName = null;
        
        if (dimensionConfigMap.get(facetFieldName) != null) {
            dimensionName = dimensionConfigMap.get(facetFieldName).getDisplayName();
        } else {
            dimensionName = facetFieldName;
        }
        
        return dimensionName;
        
    }
    
    private String getDimensionValueDisplayName(String facetValue){
        
        String dimensionValueDisplayName = null;
        
        if (dimensionValueConfigMap.get(facetValue) != null){
            dimensionValueDisplayName = dimensionValueConfigMap.get(facetValue).getDisplayName();
        } else {
            dimensionValueDisplayName = facetValue;
            
        }
        
        return dimensionValueDisplayName;
        
    }

}
